Skip to content

Fix connector routing and enhance shape-aware layout features#9

Merged
Malcolmnixon merged 17 commits into
mainfrom
fix/connector-router-shared-face-anchors
Jul 5, 2026
Merged

Fix connector routing and enhance shape-aware layout features#9
Malcolmnixon merged 17 commits into
mainfrom
fix/connector-router-shared-face-anchors

Conversation

@Malcolmnixon

Copy link
Copy Markdown
Member

This pull request introduces significant improvements to the shape-aware routing logic in the ConnectorRouter and updates the layered layout pipeline to leverage these enhancements. The changes ensure that connectors and ports respect the true geometric extents and projections of various node shapes (including folders with tabs, rounded rectangles, and notes), improving both the visual accuracy and flexibility of connector routing. Documentation and requirement tracking are updated to reflect these new behaviors, and cross-unit dependencies are now clearly documented.

ConnectorRouter and Layered Pipeline shape-awareness:

  • Enhanced ConnectorRouter to independently resolve folder tab width and height hints, allowing partial hints to maintain shape-aware routing; the router now computes connectable extents and surface projections per face for all supported shapes (Rectangle, RoundedRectangle, Note, Folder). [1] [2]
  • Updated the layered pipeline (PortDistributor, LongEdgeJoiner) to internally consume ConnectorRouter's shape-geometry resolution, ensuring consistent connectable-extent restrictions and surface projections for both cross-container and same-scope edges. [1] [2] [3]

Documentation and requirements:

  • Expanded and clarified the connector-router.md design document to describe the new shape geometry abstraction, anchor selection logic, shared-face distribution in batch routing, and the cross-unit dependency with the layered pipeline. [1] [2] [3]
  • Added and updated requirement satisfaction tables to track new requirements for shape-aware anchors and shared-face distribution in both the connector router and layered pipeline. [1] [2]
  • Registered the new design document in .reviewmark.yaml for traceability.

Default layout API simplification:

  • Simplified the LayoutEngine.Layout API to remove the redundant options parameter, ensuring all configuration is carried by the LayoutGraph itself and clarifying the algorithm resolution logic in the documentation. [1] [2] [3] [4]

Quality and compliance:

  • All relevant tests, lint, and build validations passed; regression coverage was added for width-only and height-only folder tab hints, and all required documentation artifacts were updated.

Malcolm Nixon and others added 14 commits July 4, 2026 16:16
… a box face

When several connectors converge on the same box face and their source boxes
don't overlap the target on the perpendicular axis, FacingAnchors/
AlignedCoordinate independently clamps each connector's anchor to the same
boundary point, so the connectors visually merge and overlap instead of
landing at distinct points along the face.

The batch Route(boxes, connections, options) overload now detects when a box
face is claimed by more than one connector and redistributes their anchors
evenly across the face (ordered by each connector's counterpart box
position), matching the approach already used by the layered layout's
PortDistributor for intra-package edges. The single-connection overload is
unchanged and documented as unsuitable when several connectors may share a
face.

Adds a regression test reproducing the exact box geometry from
DictionaryMark's generated diagram, confirming independent routing collapses
the three connectors onto an identical target point while batch routing
spreads them apart in source order.
…ons API

- ConnectorRouter: batch Route() now routes connections sequentially and
  treats each already-routed line segments as thin soft obstacles for
  subsequent connections, so parallel connectors fan out into separate
  corridors instead of overlapping along a shared trunk.
- HierarchicalLayoutAlgorithm.RouteCrossContainerEdges now batches all of a
  scope cross-container edges into a single ConnectorRouter.Route call
  instead of looping the single-connection overload, fixing the same
  anchor-collapse/corridor-overlap bug at the library own high-level
  entry point.
- LayoutEngine.Layout no longer takes a separate LayoutOptions argument.
  LayoutGraph is already an IPropertyHolder, so a caller-supplied options
  object at this entry point was a second, potentially conflicting way to
  configure the exact same settings the graph itself can carry. Callers now
  configure the graph directly (e.g. graph.Set(CoreOptions.Algorithm,
  "layered")); ILayoutAlgorithm.Apply own LayoutOptions parameter is
  unchanged, since it is the cascading mechanism for nested-scope options,
  not a duplicate of graph-level configuration.
- Updated LayoutEngineTests, GalleryWriter/GalleryShowcaseTests, and the
  design docs/XML docs/user guide examples to match.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ppearance

- ConnectorRouter/OrthogonalEdgeRouter: fix corridor-overlap collision between
  routed connectors sharing a corridor.
- HierarchicalLayoutAlgorithm: fix anchor-coordination gap where leaf-routed
  and cross-container edges were routed as two independent, anchor-unaware
  batches, causing multiple connectors to land on the exact same shared-face
  anchor point. Conflicted direct-direct edges are now promoted into the
  cross-container routing batch so all edges touching a shared face are
  coordinated together.
- LayoutGraphNode: add Shape, Keyword, and Compartments properties so a
  caller can select a node's full box appearance on the input graph, before
  layout, instead of only on the placed LayoutBox. LayeredLayoutAlgorithm,
  ContainmentLayoutAlgorithm, and HierarchicalLayoutAlgorithm all propagate
  the three properties unchanged onto the placed box (or view node); each
  defaults to a plain rectangle with no keyword or compartments, so existing
  flat-graph behavior is unaffected.
- Add regression tests for both routing fixes and propagation tests for the
  new box-appearance properties across all three algorithms.
- Add a "Box appearance" gallery diagram/section/test demonstrating Shape,
  Keyword, and Compartments on a folder container with keyworded and
  compartmented leaf boxes.
- Update the Layout Graph design doc, requirements, and user guide to
  document the new properties.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…thm containers

HierarchicalLayoutAlgorithm previously reserved a hardcoded 24px title band above
a labelled container's children, regardless of theme or whether the container also
carries a Keyword. At default theme metrics a container with both a label and a
keyword line actually needs ~44px, so the fixed band would let the label overflow
into the children's content.

- LayoutGraphNode: add a nullable TitleHeight override (logical pixels). Null (the
  default) preserves the existing generic default band; a caller sets it explicitly
  to match a specific theme's computed title-area height when needed.
- HierarchicalLayoutAlgorithm: resolve the title band via a new ResolveTitleHeight
  helper (node.TitleHeight ?? DefaultContainerTitleHeight), applied consistently in
  both container sizing and child-offset composition. Still gated on the container
  carrying a Label, matching existing behavior.
- Add regression tests: override replaces the default band height and offsets
  nested children accordingly; an unlabelled container ignores the override.
- Update the HierarchicalLayoutAlgorithm and Layout Graph design docs, the
  Layout Graph requirements, and the user guide.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- add shape-geometry hints and shape-aware face routing so connectors respect folder tabs and rounded-corner extents
- distribute shared-face anchors across usable extents, project folder top anchors to the recessed body top, and fall back when a natural face is unusable
- stop soft-obstacle detours from publishing leave-and-return loops and update tests and compliance documents

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds a Shape-aware connectors gallery section demonstrating the folder
tab-avoidance / surface-projection anchor fix: an external Client node
connects into a Utilities folder container from above, and the connector
correctly lands on the folder's recessed top edge instead of floating above
it or scraping the tab.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… case

The previous version connected Client to a descendant inside the folder
(a cross-container edge), which is excluded from the root scope's own leaf
view and so ignored the Direction override entirely -- Client landed beside
the folder instead of above it, exercising a right-face anchor rather than
the intended top-face tab-avoidance path.

Connect directly to the folder node itself instead: a direct root member,
so the edge belongs to the root's own leaf view and its ranking honors the
downward flow direction, reliably placing Client above the folder and
exercising the actual tab-avoidance/surface-projection behavior the section
is meant to demonstrate.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
RenderBoxTitle/RenderBoxCompartments in both SvgRenderer and
SkiaRasterRenderer positioned the keyword/label/compartment text starting
at box.Y, ignoring the folder tab's recessed body top. For wide folders
this made the title text float in the empty notch above the actual
outline instead of sitting inside the main body, contrary to standard
SysML/UML folder notation where the tab is purely decorative and never
carries text.

Both renderers now resolve the title area's top via a shared
ResolveTitleAreaTop helper that offsets by the resolved folder tab height
for Shape.Folder boxes (reusing the existing FolderTabHeight override /
BoxMetrics fallback), leaving all other shapes unaffected.

Added SvgRenderer_Render_FolderBox_TitleRecessedBelowTab regression test.
Regenerated gallery (folder-top-face-anchor.svg, box-appearance.svg) to
confirm keyword/title text now renders below the tab in both showcases.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Container children were composed using only the ordinary label/keyword
title band, so a Folder container's tab height was never reserved above
its nested children -- once the title text was recessed below the tab
(previous commit), children could overlap the recessed title.
HierarchicalLayoutAlgorithm now adds a ResolveContentOffsetHeight helper
that folds the tab height into both the container's effective size and
its children's vertical offset for Shape.Folder containers.

Also closes the note-shape gap flagged in NoteGeometry's own doc comment:
the folded top-right corner was not excluded from the connectable extent,
so a connector could anchor inside the cut-off triangle instead of the
real outline. NoteGeometry now excludes the fold-affected portion of the
top face (near the right edge) and the right face (near the top edge),
mirroring FolderGeometry's tab-exclusion pattern; the remaining extents
lie exactly on the bounding box so no projection offset is needed.

Added a new "shape-gallery" showcase diagram/section demonstrating every
BoxShape side by side, each with content appropriate to it (rectangle and
rounded-rectangle with a keyword and compartment, a folder with a nested
child, a note with free-form text), proving every shape reserves enough
space for its own content without overlapping its non-rectangular
features.

Added regression tests for both fixes and regenerated the gallery.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ConnectorRouter

Root cause: LayeredLayoutAlgorithm.Apply mapped each LayoutGraphNode to an abstract
LayerNode(Width, Height), discarding all shape metadata (Shape, FolderTabWidth,
FolderTabHeight, RoundedCornerRadius). Its own internal routing pipeline under
Engine/Layered/ (PortDistributor, LongEdgeJoiner, AxisTransform, etc.) therefore
anchored connectors purely to the plain bounding rectangle on every face, with no
awareness of shapes such as Folder (raised tab) or Note (folded corner). This is
the default leaf-edge algorithm used for the vast majority of same-scope edges,
including the common case of two siblings where one is a Folder-shaped container
(e.g. Client -> pkg in the FolderTopFaceAnchor gallery diagram), so those
connectors visibly floated above the real drawn shape outline (y=140, the
bounding-box top) instead of terminating at the recessed body-top surface
(y=164 = box.Y + tabHeight) that ConnectorRouter already computes correctly for
containment/cross-container edges.

Fix: extend LayerNode with shape metadata (Shape, RoundedCornerRadius,
FolderTabWidth, FolderTabHeight, Label, RealWidth, RealHeight), thread it through
LayeredLayoutAlgorithm.Apply and SwapNodeAxes, and reuse (rather than duplicate)
ConnectorRouter's already-tested shape geometry classes (RectangleGeometry,
RoundedRectangleGeometry, NoteGeometry, FolderGeometry, ResolveShapeGeometry) by
widening them from private to internal. Add a direction-to-real-face resolver
(Right: src=Right/tgt=Left, Left: src=Left/tgt=Right, Down: src=Bottom/tgt=Top,
Up: src=Top/tgt=Bottom) in the new ShapeAnchorSupport.cs, and use it to:

- Restrict PortDistributor's evenly-spaced port band to each shape's usable
  connectable extents on the relevant real face (instead of the full bounding
  span), for non-dummy, non-Rectangle-shaped nodes.
- Apply each shape's ProjectToSurface inward offset in LongEdgeJoiner so the
  final routed segment terminates on the true recessed/excluded-adjusted
  surface rather than the bounding box.

Plain Rectangle-shaped (and shapeless) nodes and dummy nodes (used for long-edge
splitting) take the original, unmodified code path, so existing diagrams are
unaffected (verified via full gallery regeneration - only
folder-top-face-anchor.svg changed, with the Client -> pkg connector now
terminating at y=164 instead of y=140).

Added 14 new tests across PortDistributorTests, LongEdgeJoinerTests, and
LayeredLayoutAlgorithmTests (one per LayoutDirection plus Note fold-exclusion
plus Rectangle byte-identical regression), verified the inward-projection sign
for all 4 directions x source/target by tracing AxisTransform's abstract-to-
screen mapping, and updated design docs (connector-router.md,
layered-pipeline.md, layered-layout-algorithm.md), requirements
(docs/reqstream/rendering-layout/), verification docs, and .reviewmark.yaml to
record that shape-aware anchoring now applies uniformly to both
ConnectorRouter-routed edges and LayeredLayoutAlgorithm's own leaf-edge routing.

Full test suite: 217/217 passing in Layout.Tests (was 203), no regressions in
any other project. fix.ps1/lint.ps1 clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ength

Root cause: OrthogonalEdgeRouter.AStar priced a soft-obstacle move with a
flat, boolean-gated 60-unit penalty (SoftObstaclePenalty) regardless of how
much of the move actually overlapped an already-routed connector's corridor.
In narrow-gap real-world geometry (e.g. a small box sitting just below a
much wider one across a 48px gap), the sparse Hanan grid collapses a
connector's entire multi-hundred-pixel horizontal corridor into a single
grid move, so a 500+px visual overlap cost exactly the same flat 60 units
as a trivial few-pixel overlap. Meanwhile the bounded cost of detouring to
a nearby alternate lane (roughly 2x TurnPenalty + 2x the lane offset) was
consistently a little higher than that flat 60, so A* always kept the long
overlap no matter how visually merged the result looked. This was traced
and reproduced exactly against the reported DictionaryMark diagram
(OtsDependencies -> DictionaryMark, three fan-out connectors merging onto
identical y=1271 corridors for hundreds of pixels).

Fix: replace the flat SoftObstaclePenalty with a cost proportional to the
overlap length between the candidate move and each soft obstacle. Added
SoftObstacleOverlapLength(...), mirroring SegmentBlocked's branch logic but
returning the summed overlapping length instead of a bool, and two named
constants (SoftObstacleBaseCost = 10.0, SoftObstaclePerUnitLengthCost = 1.0)
replacing the single flat penalty. A short, incidental overlap therefore
stays cheap (never worth a pointless detour, preserving the legitimate
endpoint-adjacent shared-approach-corridor behavior), while an extended
overlap becomes proportionally expensive and a bounded-cost lane change is
preferred instead - for any number of fan-out/fan-in connectors and any box
geometry, not a special case tuned to the reported 3-edge scenario.

Tests added (TDD, confirmed red against the flat-penalty code, green after
the fix):
- ConnectorRouterTests: three new batch-API regression tests reproducing
  the exact reported defect shape (narrow vertical gap, 3/5 fan-out edges,
  and a fan-in variant with distinct source boxes), asserting via a new
  AssertNoCollinearOverlapAcrossLines helper that no two connectors'
  interior segments ride the same grid line for more than a trivial span.
- OrthogonalEdgeRouterTests: RouteWithStatus_LongSoftObstacleOverlap_
  PrefersAlternateLane pins the cost-function fix directly (a synthetic
  soft obstacle spanning the entire corridor now triggers a detour), with
  a RouteWithStatus_ShortSoftObstacleOverlap_KeepsStraightRoute companion
  confirming short/incidental overlaps remain tolerated.

Full existing suite (386 tests across all TFMs) passes with zero
regressions; before the fix, 3 of the 5 new tests failed as expected (red
step) while the other 2 (short-overlap tolerance and the distinct-source-
boxes fan-in variant) already passed. `pwsh ./gallery.ps1` produced no SVG
diffs (no existing gallery sample exercises this narrow-gap geometry).

Companion artifacts updated: design docs for ConnectorRouter and
OrthogonalEdgeRouter, a new OrthogonalEdgeRouter requirement
(Rendering-Layout-OrthogonalEdgeRouter-AvoidsExtendedSoftOverlap) in the
reqstream YAML, and the corresponding verification docs (new test-scenario
bullet, requirements-coverage entry, and an additional-regression-coverage
note on the ConnectorRouter side).

Real-world verification: rebuilt SysML2Tools (Release, clean) against this
branch's Rendering source via its ProjectReference, then regenerated
DictionaryMark's DictionaryMarkSystemView.svg/.png through the SysML2Tools
CLI. Numerically confirmed in the raw SVG path data that the third
OtsDependencies -> DictionaryMark connector (previously overlapping a
second connector's corridor for 529.6px at y=1271) now takes an 8px
vertical detour to y=1263, eliminating the overlap; the other two
connectors' short endpoint-adjacent touch at y=1271 remains (by design,
non-overlapping). Visually confirmed via the regenerated PNG that the
three connectors now separate into distinct rows with no other
regressions (nesting and labels intact). DictionaryMark's generated SVG/PNG
are gitignored, so no cleanup was required there; both DictionaryMark and
SysML2Tools working trees were confirmed clean before and after.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…anchor gallery caption

The folder-top-face-anchor showcase connects two root-scope nodes directly
to the folder itself, not across a containment boundary, so describing it
as a 'cross-container edge' was inaccurate. Reworded the gallery caption,
GalleryCatalog description, and GalleryShowcaseTests doc comment to
correctly describe it as an edge approaching a folder container from above.

Found by the formal-review agent (GPT-5.4-mini) during pre-merge review
of the Rendering-Gallery review set; all other 18 affected review sets
and the full-diff code review passed with no issues.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 5, 2026 21:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request improves shape-aware diagram routing and rendering consistency across the layout pipeline by propagating per-node shape metadata and optional resolved geometry hints (rounded-corner radius, folder tab width/height) from LayoutGraphNode through layout algorithms into LayoutBox, and by enhancing both ConnectorRouter and the layered pipeline’s internal routing stages to honor connectable extents and surface projections for non-rectangular shapes. It also simplifies the default LayoutEngine.Layout facade to take configuration exclusively from the LayoutGraph, with accompanying documentation, requirements traceability, gallery outputs, and expanded regression tests.

Changes:

  • Added/propagated box appearance and shape-geometry hint fields (Shape, Keyword, Compartments, RoundedCornerRadius, FolderTabWidth, FolderTabHeight) through graph → algorithms → placed boxes, and updated SVG/Skia renderers to consume them.
  • Made same-scope layered routing shape-aware by reusing ConnectorRouter’s internal shape geometry (extent restriction + endpoint projection) in PortDistributor/LongEdgeJoiner.
  • Enhanced orthogonal routing with soft obstacles (length-proportional overlap cost) and route cleanup; simplified LayoutEngine.Layout API and updated docs/reqstream/verification + gallery artifacts accordingly.

Reviewed changes

Copilot reviewed 56 out of 59 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
test/DemaConsulting.Rendering.Tests/LayoutTests.cs Expands LayoutBox construction test to include new shape-geometry hint fields.
test/DemaConsulting.Rendering.Tests/LayoutGraphTests.cs Adds test ensuring shape-geometry hints default to null and are settable on graph nodes.
test/DemaConsulting.Rendering.Svg.Tests/SvgRendererTests.cs Adds regression test verifying folder title/keyword text is recessed below the tab.
test/DemaConsulting.Rendering.Layout.Tests/LayoutEngineTests.cs Updates tests for LayoutEngine.Layout(graph) API and graph-driven algorithm selection.
test/DemaConsulting.Rendering.Layout.Tests/LayeredLayoutAlgorithmTests.cs Adds propagation tests and multiple shape-aware routing regression tests for layered output.
test/DemaConsulting.Rendering.Layout.Tests/HierarchicalLayoutAlgorithmTests.cs Adds tests for TitleHeight override behavior, folder tab child offset, and shared-face routing coordination.
test/DemaConsulting.Rendering.Layout.Tests/Engine/OrthogonalEdgeRouterTests.cs Adds tests for soft-obstacle behavior and eliminating redundant waypoint revisits.
test/DemaConsulting.Rendering.Layout.Tests/Engine/Layered/PortDistributorTests.cs Adds shape-aware port-distribution tests (folder tab exclusion, note fold exclusion) and refactors helpers for direction.
test/DemaConsulting.Rendering.Layout.Tests/Engine/Layered/LongEdgeJoinerTests.cs Adds shape-aware endpoint projection tests and refactors helpers for direction.
test/DemaConsulting.Rendering.Layout.Tests/ContainmentLayoutAlgorithmTests.cs Adds propagation test for shape/keyword/compartments and geometry hints through containment packing.
test/DemaConsulting.Rendering.Layout.Tests/ConnectorRouterTests.cs Extends test helper to construct shaped boxes with optional geometry hints; adds many shape-aware and batching regressions.
test/DemaConsulting.Rendering.Gallery/GalleryWriter.cs Updates gallery writer to use graph-configured layout via LayoutEngine.Layout(graph).
test/DemaConsulting.Rendering.Gallery/GalleryShowcaseTests.cs Updates showcase tests for graph-driven algorithm/direction selection; adds new gallery render tests.
test/DemaConsulting.Rendering.Gallery/GalleryDiagrams.cs Adds new gallery diagrams demonstrating box appearance and shape-aware connector routing.
test/DemaConsulting.Rendering.Gallery/GalleryCatalog.cs Registers new gallery sections and SVG outputs.
src/DemaConsulting.Rendering/NamespaceDoc.cs Updates public-facing docs to reflect the new LayoutEngine.Layout(graph) facade.
src/DemaConsulting.Rendering/LayoutTree/LayoutBox.cs Extends LayoutBox to carry optional resolved shape-geometry hints.
src/DemaConsulting.Rendering/Graph/LayoutGraphNode.cs Adds Shape, Keyword, Compartments, TitleHeight, and shape-geometry hint properties to graph nodes.
src/DemaConsulting.Rendering.Svg/SvgRenderer.cs Resolves folder/tab and rounded-corner geometry from LayoutBox hints; recesses folder title area below tab.
src/DemaConsulting.Rendering.Skia/SkiaRasterRenderer.cs Mirrors SVG renderer’s hint resolution and folder title-area recession for raster output.
src/DemaConsulting.Rendering.Layout/NamespaceDoc.cs Updates layout package namespace docs to match the new facade usage.
src/DemaConsulting.Rendering.Layout/LayoutEngine.cs Removes LayoutOptions parameter from facade; resolves algorithm from graph and seeds algorithms with empty options.
src/DemaConsulting.Rendering.Layout/LayeredLayoutAlgorithm.cs Threads shape metadata/hints into engine nodes and propagates appearance/hints into placed boxes.
src/DemaConsulting.Rendering.Layout/HierarchicalLayoutAlgorithm.cs Adds container title-height override, folder tab child-offset reservation, and unified routing for cross-container + leaf edges.
src/DemaConsulting.Rendering.Layout/Engine/OrthogonalEdgeRouter.cs Adds soft obstacles, proportional overlap cost, axis expansion for soft obstacles, and revisit-removal cleanup.
src/DemaConsulting.Rendering.Layout/Engine/Layered/ShapeAnchorSupport.cs New helper bridging layered pipeline direction/axes to ConnectorRouter shape geometry APIs.
src/DemaConsulting.Rendering.Layout/Engine/Layered/PortDistributor.cs Uses ConnectorRouter shape geometry to restrict port bands for shaped nodes while keeping rectangle fast path.
src/DemaConsulting.Rendering.Layout/Engine/Layered/LongEdgeJoiner.cs Projects shaped endpoints inward to real outlines using ConnectorRouter shape geometry.
src/DemaConsulting.Rendering.Layout/Engine/Layered/LayeredGraph.cs Ensures node axis swapping preserves all new LayerNode shape metadata fields.
src/DemaConsulting.Rendering.Layout/Engine/InterconnectionLayoutEngine.cs Expands LayerNode to carry shape metadata and real (non-swapped) dimensions for geometry resolution.
src/DemaConsulting.Rendering.Layout/ContainmentLayoutAlgorithm.cs Propagates node appearance and geometry hints into packed boxes.
docs/verification/rendering/layout-tree.md Adds verification coverage for LayoutBox shape-geometry hints.
docs/verification/rendering/layout-graph.md Adds verification coverage for node shape-geometry hints storage and propagation through algorithms.
docs/verification/rendering-layout/layered-layout-algorithm.md Adds verification scenarios/requirement mapping for shape-aware routing in layered layout.
docs/verification/rendering-layout/engine/orthogonal-edge-router.md Adds verification scenarios/requirement mapping for soft obstacles and revisit prevention.
docs/verification/rendering-layout/engine/layered-pipeline.md Adds verification scenarios/requirement mapping for shape-aware anchor restriction and projection stages.
docs/verification/rendering-layout/connector-router.md Documents new shape-aware anchor behavior and shared-face distribution; notes additional regressions.
docs/user_guide/introduction.md Updates facade usage, explains appearance selection, container title height override, and folder hint partials.
docs/reqstream/rendering/layout-tree.yaml Adds requirement for placed-box shape-geometry hints.
docs/reqstream/rendering/layout-graph.yaml Adds requirements for box appearance, container title-height override, and shape-geometry hints.
docs/reqstream/rendering-layout/layered-layout-algorithm.yaml Adds requirement for shape-aware routing in LayeredLayoutAlgorithm.
docs/reqstream/rendering-layout/engine/orthogonal-edge-router.yaml Adds requirements for no waypoint revisits and avoiding extended soft-obstacle overlap.
docs/reqstream/rendering-layout/engine/layered-pipeline.yaml Adds requirement for layered pipeline shape-aware anchors/projection matching ConnectorRouter.
docs/reqstream/rendering-layout/connector-router.yaml Adds requirements for shape-aware anchors and shared-face distribution over connectable extents.
docs/gallery/shape-gallery.svg New rendered gallery artifact demonstrating all shapes with content clearance.
docs/gallery/gallery.md Adds new gallery sections for box appearance and shape-aware connectors.
docs/gallery/folder-top-face-anchor.svg New rendered gallery artifact demonstrating folder tab-aware anchoring.
docs/gallery/box-appearance.svg New rendered gallery artifact demonstrating shape/keyword/compartments appearance selection.
docs/design/rendering/layout-tree.md Updates design to include LayoutBox shape-geometry hints.
docs/design/rendering/layout-graph.md Updates design to include node appearance fields, title-height override, and geometry hints propagation.
docs/design/rendering-layout/layered-layout-algorithm.md Updates design to reflect shape-aware routing via layered pipeline.
docs/design/rendering-layout/hierarchical-layout-algorithm.md Updates design to document title-height override and default title-band behavior.
docs/design/rendering-layout/engine/orthogonal-edge-router.md Updates design to document soft obstacles, proportional overlap cost, and revisit cleanup.
docs/design/rendering-layout/engine/layered-pipeline.md Updates design to document LayerNode metadata and cross-unit dependency on ConnectorRouter geometry.
docs/design/rendering-layout/default-layout.md Updates design for new LayoutEngine API and graph-only configuration model.
docs/design/rendering-layout/connector-router.md Expands design doc for shape geometry abstraction, anchor selection, and batch distribution/steering behavior.
.reviewmark.yaml Registers connector-router design doc for review tracking/traceability.
.agent-logs/developer-connector-router-quality-retry-20260704-2205.md Adds agent execution/report log for prior work.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/DemaConsulting.Rendering.Layout/Engine/OrthogonalEdgeRouter.cs
Comment thread test/DemaConsulting.Rendering.Layout.Tests/Engine/Layered/PortDistributorTests.cs Outdated
Malcolm Nixon and others added 2 commits July 5, 2026 17:25
…API simplification

A prior commit (8a1ce80) removed the separate LayoutOptions argument from
LayoutEngine.Layout and renamed/removed several LayoutEngineTests methods,
but did not update the reqstream requirements or verification doc for the
DefaultLayout unit to match. This left 4 requirements referencing
nonexistent test names (Layout_OptionsDeclareLayered_..., Layout_
OptionsDeclareContainment_..., Layout_GraphDeclarationOverridesOptions,
Layout_NullOptions_Throws), causing CI's reqstream --enforce check to
report them unsatisfied:
  - Rendering-Layout-DefaultLayout
  - Rendering-Layout-LayoutEngine-Resolution
  - Rendering-Layout-LayoutEngine-FlatEquivalence
  - Rendering-Layout-LayoutEngine-Validation

Updated docs/reqstream/rendering-layout/default-layout.yaml and
docs/verification/rendering-layout/default-layout.md to reference the
actual current test names (Layout_GraphDeclaresLayered_..., Layout_
GraphDeclaresContainment_..., Layout_NullGraph_Throws, Layout_
NullRegistry_Throws) and to describe resolution against an explicit
graph-level declaration only, matching the simplified single-argument
Layout API. Verified locally with 'dotnet reqstream --enforce' that all
4 requirements are now satisfied; full test suite (668 tests x 3 TFMs)
and fix.ps1/lint.ps1 remain clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- OrthogonalEdgeRouter.AStar: hoist the 'softObstacles ?? []' null-coalesce
  out of the per-neighbor A* loop into a single resolved local, avoiding a
  fresh empty-array allocation on every iteration when no soft obstacles
  are supplied.
- PortDistributorTests: rename the misleadingly-named 'localX'/'localY'
  locals (derived from the internal AugPortY*/AugY axis fields) to
  'portOffsetAlongTopFace'/'portOffsetAlongRightFace' with a clarifying
  comment about the Down-direction axis swap, so the variable names match
  the real-world axis they represent rather than the internal field names
  they're read from.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 5, 2026 21:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 58 out of 61 changed files in this pull request and generated 9 comments.

Comment thread src/DemaConsulting.Rendering/Graph/LayoutGraphNode.cs Outdated
Comment thread src/DemaConsulting.Rendering.Layout/Engine/Layered/PortDistributor.cs Outdated
Comment thread src/DemaConsulting.Rendering.Svg/SvgRenderer.cs Outdated
Comment thread src/DemaConsulting.Rendering.Svg/SvgRenderer.cs Outdated
Comment thread src/DemaConsulting.Rendering.Svg/SvgRenderer.cs Outdated
Comment thread src/DemaConsulting.Rendering.Skia/SkiaRasterRenderer.cs Outdated
Comment thread src/DemaConsulting.Rendering.Skia/SkiaRasterRenderer.cs Outdated
Comment thread src/DemaConsulting.Rendering.Skia/SkiaRasterRenderer.cs Outdated
Comment thread test/DemaConsulting.Rendering.Layout.Tests/ConnectorRouterTests.cs
- LayoutGraphNode.Compartments: guard the setter so a null assignment is
  coerced to an empty list, keeping the non-nullable public property
  contract safe against a caller assigning null (only a compile-time
  warning previously, would have thrown at runtime on first iteration).
  Added a regression test proving the coercion.
- PortDistributor.Apply: the target-side port-band branch is already
  guarded by 'else' of augNodes[ni].IsDummy, so ni is always a valid real-
  node index; replaced the 'nodes[ni < n ? ni : 0]' fallback with a direct
  'nodes[ni]' and removed the now-unused 'n' local, avoiding a silent
  wrong-node computation if the invariant ever broke.
- SvgRenderer/SkiaRasterRenderer ResolveRoundedCornerRadius/
  ResolveFolderTabWidth/ResolveFolderTabHeight: clamp caller-supplied
  LayoutBox shape-geometry hints to a non-negative value before use,
  mirroring ConnectorRouter's existing hint-resolution clamping, so a
  negative hint can no longer produce invalid SVG attributes, inverted
  folder recesses, or invalid Skia rounded-rect radii.
- ConnectorRouterTests: removed a stray extra blank line for formatting
  consistency.

Verified: full test suite (669 tests x 3 TFMs) passing, fix.ps1/lint.ps1
clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Malcolmnixon
Malcolmnixon merged commit 4f0a3f4 into main Jul 5, 2026
6 checks passed
@Malcolmnixon
Malcolmnixon deleted the fix/connector-router-shared-face-anchors branch July 5, 2026 22:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants